package com.yzy.fly.config;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.yzy.fly.config.properties.FlyProperties;
import com.yzy.fly.config.properties.TransactionConfiguration;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.transaction.annotation.SpringTransactionAnnotationParser;
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;

@Slf4j
public class FlySpringTransactionAnnotationParser extends SpringTransactionAnnotationParser {

    @Getter
    private Set<String> namesPkgCls = Sets.newHashSetWithExpectedSize(0);
    @Getter
    private List<FlyTransaction> transactionInfos = Lists.newArrayListWithCapacity(0);

    private FlyProperties flyProperties;
    private TransactionConfiguration transactionConfig;

    private boolean logEnable;

    public FlySpringTransactionAnnotationParser(FlyProperties flyProperties) {
        this.flyProperties = flyProperties;
        transactionConfig = flyProperties.getConfiguration().getTransaction();
        logEnable = transactionConfig.isLogEnable();
    }

    @Override
    @Nullable
    public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
        TransactionAttribute attribute = super.parseTransactionAnnotation(element);
        if (attribute != null) {
            FlyTransaction flyTransaction = new FlyTransaction();
            transactionInfos.add(flyTransaction);
            String pkgName = "";
            String clsName = "";
            if (element instanceof Class) {
                Class cls = (Class) element;
                pkgName = cls.getPackage().getName();
                clsName = cls.getSimpleName();
                flyTransaction.setAnnType("C");

                if (logEnable) {
                    log.info("clsName:{} all of methodNames", clsName);
                }

            } else if (element instanceof Method) {
                Method method = (Method) element;
                Class cls = method.getDeclaringClass();
                pkgName = cls.getPackage().getName();
                clsName = cls.getSimpleName();
                Class<?> returnType = method.getReturnType();
                String methodName = method.getName();

                if (logEnable) {
                    log.info("clsName:{} methodName:{}", clsName, methodName);
                }

                flyTransaction.setMethodName(methodName);
                flyTransaction.setAnnType("M");
            }


            flyTransaction.setPkgName(pkgName);
            flyTransaction.setClsName(clsName);

            namesPkgCls.add(pkgName + "." + clsName);

            FlyTransactionAttribute flyTransactionAttribute = new FlyTransactionAttribute();
            flyTransaction.setAttribute(flyTransactionAttribute);

            if (attribute instanceof RuleBasedTransactionAttribute) {
                RuleBasedTransactionAttribute rbta = (RuleBasedTransactionAttribute) attribute;
//                org.springframework.transaction.annotation.Propagation
                flyTransactionAttribute.setPropagationBehavior(rbta.getPropagationBehavior());
                flyTransactionAttribute.setIsolationLevel(rbta.getIsolationLevel());
                flyTransactionAttribute.setTimeout(rbta.getTimeout());
                flyTransactionAttribute.setTimeoutString(rbta.getTimeoutString());
                flyTransactionAttribute.setReadOnly(rbta.isReadOnly());
                flyTransactionAttribute.setQualifier(rbta.getQualifier());
                flyTransactionAttribute.setLabels(Lists.newArrayList(rbta.getLabels()));

                List<String> rollbackForClassNames = Lists.newArrayListWithCapacity(0);
                List<String> noRollbackForClassNames = Lists.newArrayListWithCapacity(0);

                List<RollbackRuleAttribute> rollbackRules = rbta.getRollbackRules();
                for (int i = 0; i < rollbackRules.size(); i++) {
                    RollbackRuleAttribute rollbackRuleAttribute = rollbackRules.get(i);
                    if (rollbackRuleAttribute instanceof NoRollbackRuleAttribute) {
                        noRollbackForClassNames.add(rollbackRuleAttribute.getExceptionName());
                    } else {
                        rollbackForClassNames.add(rollbackRuleAttribute.getExceptionName());
                    }
                }
                flyTransactionAttribute.setRollbackForClassNames(rollbackForClassNames);
                flyTransactionAttribute.setNoRollbackForClassNames(noRollbackForClassNames);
            }
        }
        return attribute;
    }

}
